#include <cassert>
#include <chrono>
#include <iostream>
#include "leveldb/db.h"
#include "leveldb/comparator.h"
#include "util.h"
#include "stats.h"
#include "learned_index.h"
#include <cstring>
#include "cxxopts.hpp"
#include <unistd.h>
#include <fstream>
#include "../db/version_set.h"
#include <cmath>
#include <random>
#include <thread>
#include <vector>
#include <mutex>
//#include <stdio.h>


using namespace std::chrono;
using namespace leveldb;
//using namespace adgMod;
using std::string;
using std::cerr;
using std::cout;
using std::endl;
using std::to_string;
using std::vector;
using std::map;
using std::ifstream;
using std::string;

int num_pairs_base = 1000;
int mix_base = 20;
std::mutex mtx_prt_main;


class NumericalComparator : public Comparator {
public:
    NumericalComparator() = default;
    virtual const char* Name() const {return "adgMod:NumericalComparator";}
    virtual int Compare(const Slice& a, const Slice& b) const {
        uint64_t ia = adgMod::ExtractInteger(a.data(), a.size());
        uint64_t ib = adgMod::ExtractInteger(b.data(), b.size());
        if (ia < ib) return -1;
        else if (ia == ib) return 0;
        else return 1;
    }
    virtual void FindShortestSeparator(std::string* start, const Slice& limit) const { return; };
    virtual void FindShortSuccessor(std::string* key) const { return; };
};


//void PutAndPrefetch(int lower, int higher, vector<string>& keys) {
//    adgMod::Stats* instance = adgMod::Stats::GetInstance();
//
//    Status status;
//
//    instance->StartTimer(9);
//    for (int i = lower; i < higher; ++i) {
//        string value = generate_value(0);
//        status = db->Put(write_options, keys[i], value);
//        assert(status.ok() && "File Put Error");
//    }
//    instance->PauseTimer(9, true);
//
//    //cout << "Put Complete" << endl;
//
//
//    instance->StartTimer(10);
//    for (int i = lower; i < higher; ++i) {
//        string value;
//        status = db->Get(read_options, keys[i], &value);
//        //cout << "Get " << keys[i] << " Done" << endl;
//        assert(status.ok() && "File Get Error");
//    }
//    instance->PauseTimer(10, true);
//
//    //cout << "Prefetch Complete" << endl;
//};

enum LoadType {
    Ordered = 0,
    Reversed = 1,
    ReversedChunk = 2,
    Random = 3,
    RandomChunk = 4
};


//void f(std::vector<std::pair<int, int>>& chunks,
//       string& db_location_i, DB** db_i,
//       vector<string>& keys, string& values, std::default_random_engine e2,
//       int id) {}
void f(std::vector<std::pair<int, int>>&& chunks, string&& db_location_i,
       DB** db_i, vector<string>&& keys, string&& values,
       std::default_random_engine e2, int i) {}

// 左值传递版本
//void load_db(std::vector<std::pair<int, int>>& chunks,
//             string& db_location_i, DB** db_i,
//             vector<string>& keys, string& values,
//             int id) {
// 下面这个版本中，函数都是以右值的形式传递的，不能直接调用了，只能放在thread初始化里面用。
//using adgMod::Stats;
void load_db(string&& db_location_i, DB** db_i,
             vector<string>&& keys, string&& values,
             int id, adgMod::Stats** instance, int load_type,
             bool print_file_info) {

















  //  using namespace adgMod;

  /*
  chunks: 要放入数据库的K/V对
   db_location_i: 数据库的磁盘文件要保存的位置
   db_i：数据库实例的指针，是一个DB*类型
   keys: chunks中key的组合
   values: 1024个'0'
   e2: 关于随机数的东西
   id: 线程序号
 */
  // 先删掉原来的数据库持久化文件。
  string command = "rm -rf " + db_location_i;
  system(command.c_str());  //应该是STL的string不能command执行，故换成自带的string格式

  Status status;
  std::default_random_engine e2(255);

  Options options;
  ReadOptions& read_options = adgMod::read_options;
  WriteOptions& write_options = adgMod::write_options;
  options.create_if_missing = true;
  write_options.sync = false;
  (*instance)->ResetAll();

  status = DB::Open(options, db_location_i, db_i);
  assert(status.ok() && "Open Error");

  (*instance)->StartTimer(9);
  int cut_size;    // 小于10万和大于10万分开讨论
  if (keys.size() < 100000){
    cut_size = keys.size() / 1000;
    std::cerr << "every 10k as a chunk; " << keys.size() << "keys in total, devided to " << cut_size << "chunks" << endl;
  } else{
    cut_size = keys.size() / 100000;
    std::cerr << "every 0.1M as a chunk; " << keys.size() << "keys in total, devided to " << cut_size << "chunks" << endl;
  }
  std::vector<std::pair<int, int>> chunks;
  switch (load_type) {
    case Ordered: {
      for (int cut = 0; cut < cut_size; ++cut) {
        chunks.emplace_back(keys.size() * cut / cut_size, keys.size() * (cut + 1) / cut_size);
      }  // chunks每一份的大小为keys.size()/cut_size，在无法整除时，每一份大小会比10w略多
      break;
    }
    case ReversedChunk: {
      for (int cut = cut_size - 1; cut >= 0; --cut) {
        chunks.emplace_back(keys.size() * cut / cut_size, keys.size() * (cut + 1) / cut_size);
      }
      break;
    }
    case Random: {
      std::random_shuffle(keys.begin(), keys.end());
      for (int cut = 0; cut < cut_size; ++cut) {
        chunks.emplace_back(keys.size() * cut / cut_size, keys.size() * (cut + 1) / cut_size);
      }
      break;
    }
    case RandomChunk: {
      for (int cut = 0; cut < cut_size; ++cut) {
        chunks.emplace_back(keys.size() * cut / cut_size, keys.size() * (cut + 1) / cut_size);
      }
      std::random_shuffle(chunks.begin(), chunks.end());
      break;
    }
    default: assert(false && "Unsupported load type.");
  }

  std::uniform_int_distribution<uint64_t > uniform_dist_value(0, (uint64_t) values.size() - adgMod::value_size - 1);
  for (int cut = 0; cut < chunks.size(); ++cut) {  // todo: 注意这里的++cut是循环进行一轮之后执行的，不是进入循环之后执行
    for (int i = chunks[cut].first; i < chunks[cut].second; ++i) {
      status = (*db_i)->Put(write_options, keys[i],
                       {values.data() + uniform_dist_value(e2),
                        (uint64_t)adgMod::value_size});
      assert(status.ok() && "File Put Error");  // todo: 每一个chunk检查下是否有error？意义何在，为什么一个chunk检查一次？
    }
  }
//  using namespace adgMod;
//  using namespace leveldb;

  adgMod::db->vlog->Sync();
  (*instance)->PauseTimer(9, true);
  cout << "Put Complete" << std::endl;

  keys.clear();

  if (print_file_info) (*db_i)->PrintFileInfo();
  adgMod::db->WaitForBackground();
  delete *db_i;  // 这里面涉及了adgMod的封装，adgMod是全局的，很难改
}

//void learn_db(string&& db_location, DB** db) {
//  Status status;
//  Options options;  // todo: operations少了很多，比如write, synthetic等
//  status = DB::Open(options, db_location, db);
//  adgMod::db->WaitForBackground();
//  if (adgMod::MOD == 6 || adgMod::MOD == 7) { //如果设定了，跑我们修改后的版本
//    Version* current = adgMod::db->versions_->current();
//    for (int i = 1; i < config::kNumLevels; ++i) {  // 默认是7个 level
//      LearnedIndexData::Learn(new VersionAndSelf{current, adgMod::db->version_count, current->learned_index_data_[i].get(), i});
//    }
//    current->FileLearn();
//  }
//  adgMod::db->WaitForBackground();
//  delete (*db);  //delete db的时候model和fmodel的模型才会被保存。
//}


int main(int argc, char *argv[]) {
    int num_operations, num_iteration, num_mix;
    float test_num_segments_base;
    float num_pair_step;
    string db_location, profiler_out, input_filename, distribution_filename, ycsb_filename;
    bool print_single_timing, print_file_info, evict, unlimit_fd, use_distribution = false, pause, use_ycsb = false;
    bool change_level_load, change_file_load, change_level_learning, change_file_learning;
    int load_type, insert_bound, length_range;
    string db_location_copy;
    using namespace adgMod;

    cxxopts::Options commandline_options("leveldb read test", "Testing leveldb read performance.");
    commandline_options.add_options()
            ("n,get_number", "the number of gets (to be multiplied by 1024)", cxxopts::value<int>(num_operations)->default_value("1000"))
            ("s,step", "the step of the loop of the size of db", cxxopts::value<float>(num_pair_step)->default_value("1"))
            ("i,iteration", "the number of iterations of a same size", cxxopts::value<int>(num_iteration)->default_value("1"))
            ("m,modification", "if set, run our modified version", cxxopts::value<int>(adgMod::MOD)->default_value("0"))
            ("h,help", "print help message", cxxopts::value<bool>()->default_value("false"))
            ("d,directory", "the directory of db", cxxopts::value<string>(db_location)->default_value("/mnt/ssd/testdb"))
            ("k,key_size", "the size of key", cxxopts::value<int>(adgMod::key_size)->default_value("8"))
            ("v,value_size", "the size of value", cxxopts::value<int>(adgMod::value_size)->default_value("8"))
            ("single_timing", "print the time of every single get", cxxopts::value<bool>(print_single_timing)->default_value("false"))
            ("file_info", "print the file structure info", cxxopts::value<bool>(print_file_info)->default_value("false"))
            ("test_num_segments", "test: number of segments per level", cxxopts::value<float>(test_num_segments_base)->default_value("1"))
            ("string_mode", "test: use string or int in model", cxxopts::value<bool>(adgMod::string_mode)->default_value("false"))
            ("e,model_error", "error in modesl", cxxopts::value<uint32_t>(adgMod::model_error)->default_value("8"))
            ("f,input_file", "the filename of input file", cxxopts::value<string>(input_filename)->default_value(""))
            ("multiple", "test: use larger keys", cxxopts::value<uint64_t>(adgMod::key_multiple)->default_value("1"))
            ("w,write", "writedb", cxxopts::value<bool>(fresh_write)->default_value("false"))
            ("c,uncache", "evict cache", cxxopts::value<bool>(evict)->default_value("false"))
            ("u,unlimit_fd", "unlimit fd", cxxopts::value<bool>(unlimit_fd)->default_value("false"))
            ("x,dummy", "dummy option")
            ("l,load_type", "load type", cxxopts::value<int>(load_type)->default_value("0"))
            ("filter", "use filter", cxxopts::value<bool>(adgMod::use_filter)->default_value("false"))
            ("mix", "mix read and write", cxxopts::value<int>(num_mix)->default_value("0"))
            ("distribution", "operation distribution", cxxopts::value<string>(distribution_filename)->default_value(""))
            ("change_level_load", "load level model", cxxopts::value<bool>(change_level_load)->default_value("false"))
            ("change_file_load", "enable level learning", cxxopts::value<bool>(change_file_load)->default_value("false"))
            ("change_level_learning", "load file model", cxxopts::value<bool>(change_level_learning)->default_value("false"))
            ("change_file_learning", "enable file learning", cxxopts::value<bool>(change_file_learning)->default_value("false"))
            ("p,pause", "pause between operation", cxxopts::value<bool>(pause)->default_value("false"))
            ("policy", "learn policy", cxxopts::value<int>(adgMod::policy)->default_value("0"))
            ("YCSB", "use YCSB trace", cxxopts::value<string>(ycsb_filename)->default_value(""))
            ("insert", "insert new value", cxxopts::value<int>(insert_bound)->default_value("0"))
            ("range", "use range query and specify length", cxxopts::value<int>(length_range)->default_value("0"));
    auto result = commandline_options.parse(argc, argv);
//    freopen("reopen.txt", "w", stdout);
    printf("test file\n");
    if (result.count("help")) {
        printf("%s", commandline_options.help().c_str());
        exit(0);
    }

    std::default_random_engine e1(0), e2(255), e3(0);
    srand(0);
    num_operations *= num_pairs_base;  //1000 * 1000,什么意思呢？
    db_location_copy = db_location;

    adgMod::fd_limit = unlimit_fd ? 1024 * 1024 : 1024;  //TODO: 需要测试一下是否真的影响性能
    adgMod::restart_read = true;
    adgMod::level_learning_enabled ^= change_level_learning;
    adgMod::file_learning_enabled ^= change_file_learning;
    adgMod::load_level_model ^= change_level_load;
    adgMod::load_file_model ^= change_file_load;

    // adgMod::file_learning_enabled = false;


    vector<string> keys;
    vector<uint64_t> distribution;  //uint64_t是unsigned long int的typedef别名
    vector<int> ycsb_is_write;
    //keys.reserve(100000000000 / adgMod::value_size);

    //开始读取数据
    std::cerr << "读取keys..." << "\r";
    if (!input_filename.empty()) {
        ifstream input(input_filename);
        string key;
        while (input >> key) {
            string the_key = generate_key(key);
            keys.push_back(std::move(the_key));
        }
        //adgMod::key_size = (int) keys.front().size();
    } else {
        std::uniform_int_distribution<uint64_t> udist_key(0, 999999999999999);
        for (int i = 0; i < 10000000; ++i) {
            keys.push_back(generate_key(to_string(udist_key(e2))));
        }
    }

    if (!distribution_filename.empty()) {
        use_distribution = true;
        ifstream input(distribution_filename);
        uint64_t index;
        while (input >> index) {
            distribution.push_back(index);
        }
    }

    if (!ycsb_filename.empty()) {
        use_ycsb = true;
        use_distribution = true;
        ifstream input(ycsb_filename);
        uint64_t index;
        int is_write;
        while (input >> is_write >> index) {
            distribution.push_back(index);
            ycsb_is_write.push_back(is_write);
        }
    }
    bool copy_out = num_mix != 0 || use_ycsb;

    // 在load_ar的时候copy_out是false
    adgMod::Stats* instance = adgMod::Stats::GetInstance();
    vector<vector<size_t>> times(20);
    string values(1024 * 1024, '0');

    if (copy_out) {
        system("sync; echo 3 | sudo tee /proc/sys/vm/drop_caches");
    }

    if (num_mix > 1000) {
        mix_base = 1000;
        num_mix -= 1000;
    }

    for (size_t iteration = 0; iteration < num_iteration; ++iteration) {
        if (copy_out) {
            system("sudo fstrim -a -v");
        }

//        std::cerr << "第 " << iteration << " /共 " << num_iteration << " 个iteration" << endl;

        db_location = db_location_copy;  //todo: 这个怎么复制过来复制过去的？
        std::uniform_int_distribution<uint64_t > uniform_dist_file(0, (uint64_t) keys.size() - 1);
        std::uniform_int_distribution<uint64_t > uniform_dist_file2(0, (uint64_t) keys.size() - 1);
        std::uniform_int_distribution<uint64_t > uniform_dist_value(0, (uint64_t) values.size() - adgMod::value_size - 1);
        // std::uniform_int_distribution模板定义了分布对象，它返回的是均匀分布在闭合范围[a, b]内的随机整数。模板的参数类型是生成的整数类型，默认是int：
        //定义在类中的类型别名result_type和分布所生成的值的类型相对应。模板参数只接受整数类型。


//        DB* db_1;
        DB* db;
//        Options options;
//        ReadOptions& read_options = adgMod::read_options;
//        WriteOptions& write_options = adgMod::write_options;
//        // 可能重复status
//        Status status;

//        options.create_if_missing = true;
        //options.comparator = new NumericalComparator;
        //adgMod::block_restart_interval = options.block_restart_interval = adgMod::MOD == 8 || adgMod::MOD == 7 ? 1 : adgMod::block_restart_interval;
        //read_options.fill_cache = true;
//        write_options.sync = false;
//        instance->ResetAll();

        std::string db_location_1{"../ssd/db_idf_758_1"};


        if (fresh_write && iteration == 0) {
//            system("sudo fstrim -a -v");  //这句话要sudo权限， 否则不执行， 最好跑的时候就sudo，或保证当前用户可以不输密码跑sudo

            system("sync; echo 3 | sudo tee /proc/sys/vm/drop_caches");
            cout << "delete and trim complete" << endl;

            // DB open in function load_db
//            status = DB::Open(options, db_location, &db);

            // Status={leveldb::Status} < state_={const char*}NULL >
//            assert(status.ok() && "Open Error");

            // delete these soon...20220516

            // 第一种方式：直接在main中顺序执行
//            string command = "rm -rf " + db_location;
//            system(command.c_str());  //应该是STL的string不能command执行，故换成自带的string格式
//
//            Status status;
//            std::default_random_engine e2(255);
//
//            Options options;
//            ReadOptions& read_options = adgMod::read_options;
//            WriteOptions& write_options = adgMod::write_options;
//            options.create_if_missing = true;
//            write_options.sync = false;
//
//            status = DB::Open(options, db_location, &db);
//            assert(status.ok() && "Open Error");
//
//            std::uniform_int_distribution<uint64_t > uniform_dist_value(0, (uint64_t) values.size() - adgMod::value_size - 1);
//
//            for (int cut = 0; cut < chunks.size(); ++cut) {  // todo: 注意这里的++cut是循环进行一轮之后执行的，不是进入循环之后执行
//              std::cerr << "leveldb writing process: " << cut << " / " << chunks.size() << "\r";
//              for (int i = chunks[cut].first; i < chunks[cut].second; ++i) {
//                status = db->Put(write_options, keys[i],
//                                      {values.data() + uniform_dist_value(e2),
//                                       (uint64_t)adgMod::value_size});
//                assert(status.ok() && "File Put Error");  // todo: 每一个chunk检查下是否有error？意义何在，为什么一个chunk检查一次？
//              }
//            }
//            std::cerr << "thread: "  << "装进去了" << std::endl;
//            adgMod::db->vlog->Sync();
//            std::cerr << "thread: "  << "Sync好了" << std::endl;
//            adgMod::db->WaitForBackground();
//            std::cerr << "thread: "  << "wait BG好了" << std::endl;
//            delete db;
//            std::cerr << "thread: "  << "delete好了" << std::endl;

            // 第二种方式，包装成函数，左值引用的传参方式
//             load_db(chunks, db_location, &db, keys, values, 0);  // 要改db，所以传他的引用&db，形参里面加个指针db*, db本身是DB*，所以最后是 DB** db_i。

            // 第三种方式，包装成右值专递的函数，送入thread线程中
            std::vector<std::thread> threads(2);
            // delete db阶段涉及到adgMod::下面的元素的操作，使得两个线程无法同时进行，甚至两个delete本身作为整块（加锁）的先后都不行，所以两个线程依次join

            threads[0] = std::thread(load_db, db_location, &db, keys, values, 0,
                                             &instance, load_type, print_file_info);
            threads[0].join();
//            threads[1] = std::thread(load_db, db_location_1, &db_1, keys, values, 1);
//            threads[0].join();
//            threads[1].join();

            // 应该是keys没加锁？

//            for (int i=0; i<2; i++) {
//              threads[i].join();
//            }


            cout << "key clear好了" << endl;

            if (print_file_info && iteration == 0) db->PrintFileInfo();
//            if (print_file_info && iteration == 0) db_1->PrintFileInfo();

            // learn_db1： 顺序代码
            Status status;
            Options options;
//            ReadOptions& read_options = adgMod::read_options;
//            WriteOptions& write_options = adgMod::write_options;
//            options.create_if_missing = true;
//            options.comparator = new NumericalComparator;  // options.comparator不能随便换，换了要么status出问题，要么数据库出问题（free(): double free detected in tcache 2）
//            read_options.fill_cache = true;
//            write_options.sync = false;
            status = DB::Open(options, db_location, &db);
            adgMod::db->WaitForBackground();
            if (adgMod::MOD == 6 || adgMod::MOD == 7) { //如果设定了，跑我们修改后的版本
                Version* current = adgMod::db->versions_->current();
                for (int i = 1; i < config::kNumLevels; ++i) {  // 默认是7个 level
                    LearnedIndexData::Learn(new VersionAndSelf{current, adgMod::db->version_count, current->learned_index_data_[i].get(), i});
                }
                current->FileLearn();
            }
            adgMod::db->WaitForBackground();
            delete db;  //delete db的时候model和fmodel的模型才会被保存。

            // learn_db2：包装，thread传递代码
//            std::cerr << "开始双线程learn" << std::endl;
//            std::thread th1 = std::thread(learn_db, db_location, &db);
//            th1.join();
//
//            std::thread th2 = std::thread(learn_db, db_location_1, &db_1);
//
////            th1.join();
//            th2.join();

//            下面是重新加载key的代码，就几句不需要整合
            if (!input_filename.empty()) {
                ifstream input(input_filename);
                string key;
                while (input >> key) {
                    string the_key = generate_key(key);
                    keys.push_back(std::move(the_key));
                }
                adgMod::key_size = (int) keys.front().size();
            }
            fresh_write = false;
        }


        if (copy_out) {
            string db_location_mix = db_location + "_mix";
            string remove_command = "rm -rf " + db_location_mix;
            string copy_command = "cp -r " + db_location + " " + db_location_mix;

            system(remove_command.c_str());
            system(copy_command.c_str());
            db_location = db_location_mix;
        }

        // evict policy: 缓存淘汰策略
        if (evict) system("sync; echo 3 | sudo tee /proc/sys/vm/drop_caches");  //evict:

        // 第三大块：测试db
        // 第一种：main中的顺序代码
        cout << "Starting up" << endl;  // time after starting up is very long.
        Status status;
        Options options;
        ReadOptions& read_options = adgMod::read_options;
        WriteOptions& write_options = adgMod::write_options;
        options.create_if_missing = true;
//        options.comparator = new NumericalComparator;  // 同理，comparator不能随便换
        read_options.fill_cache = true;
        write_options.sync = false;

        status = DB::Open(options, db_location, &db);
        adgMod::db->WaitForBackground();
        assert(status.ok() && "Open Error");

        uint64_t last_read = 0, last_write = 0;
        int last_level = 0, last_file = 0, last_baseline = 0, last_succeeded = 0, last_false = 0, last_compaction = 0, last_learn = 0;
        std::vector<uint64_t> detailed_times;
        bool start_new_event = true;

        instance->StartTimer(13);
        uint64_t write_i = 0;
//        std::cerr << "line 483 reached, 开始测试" << endl;
//        if ((num_operations > distribution.size()) && (distribution.size() != 0) ) {
//            num_operations = distribution.size();
//        }
        for (int i = 0; i < num_operations; ++i) {
//            std::cerr << "current i: " << i << endl;
            if (i % 10000 == 0){
                std::cerr << "iteration: " << iteration << ", operation percent: " << i << "/" << num_operations << "\r";
            }
//            std::cerr << "operation percent: " << i/100 << endl;

            if (start_new_event) {
                detailed_times.push_back(instance->GetTime());
                start_new_event = false;
            }

            // todo: 这个write是怎么判断的
            // todo: length_range 又是为什么这么判断
            // 这里的代码实现上，如果ycsb_is_write = 1或2，表示要写（insert或update把应该是）
            // 如果ycsb_is_write不是1或2，表示它表示范围查询，值为范围起始值+100
            // 后面的length_range应该是表示不使用ycsb的时候，length_range自己指定
            bool write = use_ycsb ? ycsb_is_write[i] == 1 || ycsb_is_write[i] == 2 : (i % mix_base) < num_mix;
            length_range = use_ycsb && ycsb_is_write[i] > 2 ? ycsb_is_write[i] - 100 : length_range;
            Iterator* db_iter = length_range == 0 ? nullptr : db->NewIterator(read_options);


            if (write) {
                if (input_filename.empty()) {  // wirte && input_filename为空
//                    throw "shouldn't go this way";
                    instance->StartTimer(10);
                    status = db->Put(write_options, generate_key(to_string(distribution[i])), {values.data() + uniform_dist_value(e3), (uint64_t) adgMod::value_size});
                    instance->PauseTimer(10);
                } else {  // write && input_filename不为空
                    uint64_t index;
                    if (use_distribution) {  // write && input_filename不为空 && -YCSB不为空
                        index = distribution[i];
                    } else if (load_type == 0) {  // write && input_filename不为空 && -YCSB为空
                        index = write_i++ % keys.size();
                    } else {
                        index = uniform_dist_file(e1) % (keys.size() - 1);
                    }

                    instance->StartTimer(10);
                    if (use_ycsb && ycsb_is_write[i] == 2) {  // write && input_filename不为空 && -YCSB不为空，对应插入INSERT部分：
                        // 送入的文本文件是key本身，送入数据库中的是10G + key
                        status = db->Put(write_options, generate_key(to_string(10000000000 + index)), {values.data() + uniform_dist_value(e3), (uint64_t) adgMod::value_size});
                    } else { // write && input_filename不为空 && ycsb为空或ycsb对应更新UPDATE部分：
                        // 送入的文本文件是一个index，送入数据库中的是从input_filename文件中找到该index对应key
                        status = db->Put(write_options, keys[index], {values.data() + uniform_dist_value(e3), (uint64_t) adgMod::value_size});
                    }
                    instance->PauseTimer(10);
                    assert(status.ok() && "Mix Put Error");
                    //cout << index << endl;
                }
            } else if (length_range != 0) {  // wirte=false, length_range!=0, 对应SCAN。
//                throw "shouldn't go this way";
                // Seek
                if (input_filename.empty()) {
                    instance->StartTimer(4);
                    const string& key = generate_key(to_string(distribution[i]));
                    db_iter->Seek(key);
                    instance->PauseTimer(4);
                } else {
                    uint64_t index = use_distribution ? distribution[i] : uniform_dist_file2(e2) % (keys.size() - 1);
                    if (insert_bound != 0 && index > insert_bound) {  // (length_range: 81)181 (index)6497333951169773611 形状的
                        const string& key = generate_key(to_string(10000000000 + index));  // todo: 有个隐患是，index+10G后可能会爆掉19位数，导致内存被乱改？或者代码逻辑出错？

                        instance->StartTimer(4);
                        db_iter->Seek(key);
                        instance->PauseTimer(4);
                    } else {  // (length_range: 09)109 (index)7110 形状的
                        index = index >= length_range ? index - length_range : 0;
                        const string& key = keys[index];

                        instance->StartTimer(4);
                        db_iter->Seek(key);
                        instance->PauseTimer(4);
                    }


                }

                // Range
                instance->StartTimer(17);
                for (int r = 0; r < length_range; ++r) {
                    if (!db_iter->Valid()) break;
                    Slice key = db_iter->key();
                    string value = db_iter->value().ToString();
                    // cout << key.ToString() << value << endl;
                    // value.clear();
                    db_iter->Next();
                }
                instance->PauseTimer(17);
            } else {  // 这里对应非write，并且length range == 0
//                throw "shouldn't go this way";
                string value;
                if (input_filename.empty()) {
                    instance->StartTimer(4);
                    status = db->Get(read_options, generate_key(to_string(distribution[i])), &value);
                    instance->PauseTimer(4);
                    if (!status.ok()) {
                        cout << distribution[i] << " Not Found" << endl;
                        //assert(status.ok() && "File Get Error");
                    }
                } else { // 这里对应非write，并且length range == 0; 并且input_filename非空。但仍旧，570行的insert_bound无缘由。
                    uint64_t index = use_distribution ? distribution[i] : uniform_dist_file2(e2) % (keys.size() - 1);
                    // deprecated const string& key;  // 这一行key的赋值应该移到下文中的else里面，index可能是key本身，因此在keys中不一定索引地到
                    instance->StartTimer(4);
                    if (insert_bound != 0 && index > insert_bound) {  //TODO：这里读超出范围的条目是自动生成来补的，不太好
                        const string& key = generate_key(to_string(10000000000 + index));
                        status = db->Get(read_options, key, &value);
                        if (!status.ok()) {
                            cout << key << " Not Found" << endl;
                        }
                    } else {
                        const string& key = keys[index];
                        status = db->Get(read_options, key, &value);
                        if (!status.ok()) {
                            cout << key << " Not Found" << endl;
                        }
                    }

                    instance->PauseTimer(4);
                }
            }

            if (pause) {
                if ((i + 1) % (num_operations / 10000) == 0) ::usleep(800000);
            }

            if ((i + 1) % (num_operations / 100) == 0) detailed_times.push_back(instance->GetTime());
            if ((i + 1) % (num_operations / 10) == 0) {
                int level_read = levelled_counters[0].Sum();
                int file_read = levelled_counters[1].Sum();
                int baseline_read = levelled_counters[2].Sum();
                int succeeded_read = levelled_counters[3].NumSum();
                int false_read = levelled_counters[4].NumSum();

                compaction_counter_mutex.Lock();
                int num_compaction = events[0].size();
                compaction_counter_mutex.Unlock();
                learn_counter_mutex.Lock();
                int num_learn = events[1].size();
                learn_counter_mutex.Unlock();

                uint64_t read_time = instance->ReportTime(4);
                uint64_t write_time = instance->ReportTime(10);
                std::pair<uint64_t, uint64_t> time = {detailed_times.front(), detailed_times.back()};

                events[2].push_back(new WorkloadEvent(time, level_read - last_level, file_read - last_file, baseline_read - last_baseline,
                                                      succeeded_read - last_succeeded, false_read - last_false, num_compaction - last_compaction, num_learn - last_learn,
                                                      read_time - last_read, write_time - last_write, std::move(detailed_times)));

                last_level = level_read;
                last_file = file_read;
                last_baseline = baseline_read;
                last_succeeded = succeeded_read;
                last_false = false_read;
                last_compaction = num_compaction;
                last_learn = num_learn;
                last_read = read_time;
                last_write = write_time;
                detailed_times.clear();
                start_new_event = true;
                cout << (i + 1) / (num_operations / 10) << endl;
                Version* current = adgMod::db->versions_->current();
                printf("LevelSize %d %d %d %d %d %d\n", current->NumFiles(0), current->NumFiles(1), current->NumFiles(2), current->NumFiles(3),
                       current->NumFiles(4), current->NumFiles(5));
            }
            delete db_iter;


        }
//        std::cerr << "\n测试完成 at line 628" << endl;
//        main中顺序代码结束

        instance->PauseTimer(13, true);



        instance->ReportTime();
        for (int s = 0; s < times.size(); ++s) {
            times[s].push_back(instance->ReportTime(s));
        }
        adgMod::db->WaitForBackground();
        sleep(10);



        for (auto& event_array : events) {
            for (Event* e : event_array) e->Report();
        }

        for (Counter& c : levelled_counters) c.Report();

        file_data->Report();

        for (auto it : file_stats) {
            printf("FileStats %d %d %lu %lu %u %u %lu %d\n", it.first, it.second.level, it.second.start,
                   it.second.end, it.second.num_lookup_pos, it.second.num_lookup_neg, it.second.size, it.first < file_data->watermark ? 0 : 1);
        }

        adgMod::learn_cb_model->Report();

//        std::cerr << "line 658: 最后测试完成后--delete db_iter: 开始" << endl;
        auto start_deldbiter_end = system_clock::now();
        time_point<system_clock> end_deldbiter_end = system_clock::now();
        std::chrono::duration<double> elapsed_deldbiter_end = end_deldbiter_end - start_deldbiter_end;
//        std::cerr << "line 663: 最后测试完成后--delete db_iter：结束，用时: " << elapsed_deldbiter_end.count() << "s" <<  endl;

//        std::cerr << "line 665: 最后测试完成后--delete db: 开始" << "\r";
        auto start_deldb_end = system_clock::now();
        // db是一个{leveldb::DBImpl *}指针，在其执行析构函数的时候，也即leveldb::DBImpl::~DBImpl时调用delete versions_
        // versions_是一个
        delete db;
        time_point<system_clock> end_deldb_end = system_clock::now();
        std::chrono::duration<double> elapsed_deldb_end = end_deldb_end - start_deldb_end;
//        std::cerr << "line 670: 最后测试完成后--delete db：结束，用时: " << elapsed_deldb_end.count() << "s" <<  endl;
    }

    for (int s = 0; s < times.size(); ++s) {
        vector<uint64_t>& time = times[s];
        vector<double> diff(time.size());
        if (time.empty()) continue;

        double sum = std::accumulate(time.begin(), time.end(), 0.0);
        double mean = sum / time.size();
        std::transform(time.begin(), time.end(), diff.begin(), [mean] (double x) { return x - mean; });
        double stdev = std::sqrt(std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0) / time.size());

        printf("Timer %d MEAN: %lu, STDDEV: %f\n", s, (uint64_t) mean, stdev);
    }

    if (num_iteration > 1) {
        cout << "Data Without the First Item" << endl;
        for (int s = 0; s < times.size(); ++s) {
            vector<uint64_t>& time = times[s];
            vector<double> diff(time.size() - 1);
            if (time.empty()) continue;

            double sum = std::accumulate(time.begin() + 1, time.end(), 0.0);
            double mean = sum / (time.size() - 1);
            std::transform(time.begin() + 1, time.end(), diff.begin(), [mean] (double x) { return x - mean; });
            double stdev = std::sqrt(std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0) / time.size());

            printf("Timer %d MEAN: %lu, STDDEV: %f\n", s, (uint64_t) mean, stdev);
        }
    }
}


// load_db.sh
// 参数为 -f ../db/dataset/osm_ny.txt -k 16 -v 64 -d ../ssd/db_osm -m 7 -w > ../evaluation/osm_put.txt
// -f ../seg_gather/seg_0_load_txn.txt -k 16 -v 64 -d ../ssd/db_seg_0_load_txn -m 7 -w > ../evaluation/seg_0_load_txn_put.txt

// file粒度
// -f ../db/dataset/osm_ny.txt -k 16 -v 64 -d ../ssd/db_osm -m 7 -u -n 10000 -i 5 $* --change_level_load > ../evaluation/osm_llsm_fl.txt
// -f ../db/dataset/osm_ny.txt -k 16 -v 64 -d ../ssd/db_osm -m 7 -u -n 10000 -i 5 $* > ../evaluation/osm_llsm_lv.txt


// 换数据集，换成idf_758_uint32
// idf_758这玩意，本身就有问题
// 参数为 -f ../index_parsed_txt/idf_758.txt -k 16 -v 64 -d ../ssd/db_idf_758 -m 7 -w > ../evaluation/idf_758_put.txt
// 参数为 -f ../index_parsed_txt/idf_758.txt -k 16 -v 64 -d ../ssd/db_idf_758_random -m 7 -w -l 3 > ../evaluation/idf_758_random_put.txt

// 换数据集成ycsb_workloada, key变成19位
// TODO：这个本来就是乱序的，就算是sequence, 查找的时候不一定对得上这个先后顺序吧？
// 参数为 -f ../db/ycsb/fast22-300M/workloada-load-10000000-10000000_only_keys.txt -k 19 -v 64 -d ../ssd/workloada-load_sequence -m 7 -w > ../evaluation/workloada-load_sequence_put.txt
// 参数为 -f ../db/ycsb/fast22-300M/workloada-load-10000000-10000000_only_keys.txt -k 19 -v 64 -d ../ssd/workloada-load_random -m 7 -w -l 3 > ../evaluation/workloada-load_random_put.txt

// ycsb的测试：
//./read_cold -k 19 -v 64 -d ../ssd/workloada-load_sequence -m 7 -u -n 10000 --change_level_load -i 5 $* --YCSB ../db/ycsb/fast22-300M/workloada-run-10000000-10000000_ops_plus_keys.txt > ../evaluation/ycsb_llsm_workloada-run.txt



//most recent 12.14
// ./read_cold -f ../db/dataset/lognormal_200M_unique.txt -k 16 -v 64 -d ../ssd/db_lognormal_200M_unique -m 7 -w > ../evaluation/lognormal_200M_unique_put.txt

// ./read_cold -f ../db/dataset/lognormal_200M_2M.txt -k 16 -v 64 -d ../ssd/db_lognormal_200M_2M -m 7 -w > ../evaluation/lognormal_200M_2M_put.txt
// ./read_cold -f ../db/dataset/uniform_dense_200M_2M.txt -k 16 -v 64 -d ../ssd/db_uniform_dense_200M_2M -m 7 -w > ../evaluation/uniform_dense_200M_2M_put.txt


// run_dataset.sh
// ./read_cold -f ../db/dataset/osm_ny.txt -k 16 -v 64 -d ../ssd/db_osm -m 7 -u -n 10000 -i 5 $* --change_level_load > ../evaluation/osm_llsm.txt
// ./read_cold -f ../db/dataset/osm_ny.txt -k 16 -v 64 -d ../ssd/db_osm -m 8 -u -n 10000 -i 5 $* --change_level_load > ../evaluation/osm_baseline.txt
// ./read_cold -f ../db/dataset/osm_ny.txt -k 16 -v 64 -d ../ssd/db_osm -m 7 -u -n 10000 -i 5 $* > ../evaluation/osm_llsm_level.txt
//换数据集
// ./read_cold -f ../index_parsed_txt/idf_758.txt -k 16 -v 64 -d ../ssd/db_idf_758 -m 7 -u -n 10000 -i 5 $* --change_level_load > ../evaluation/idf_758_llsm.txt
// ./read_cold -f ../index_parsed_txt/idf_758.txt -k 16 -v 64 -d ../ssd/db_idf_758 -m 8 -u -n 10000 -i 5 $* --change_level_load > ../evaluation/idf_758_baseline.txt
// ./read_cold -f ../index_parsed_txt/idf_758.txt -k 16 -v 64 -d ../ssd/db_idf_758 -m 7 -u -n 10000 -i 5 $* > ../evaluation/idf_758_llsm_level.txt

// most recent trings
//./read_cold -f ../db/dataset/lognormal_200M.txt -k 16 -v 64 -d ../ssd/db_lognormal_200M -m 7 -u -n 10000 -i 1 $* > ../evaluation/lognormal_200M_llsm_level.txt
//./read_cold -f ../db/dataset/uniform_dense_200M.txt -k 16 -v 64 -d ../ssd/db_uniform_dense_200M -m 7 -u -n 10 -i 1 $* > ../evaluation/osm_llsm_level.txt
//./read_cold -f ../db/dataset/osm_ny.txt -k 16 -v 64 -d ../ssd/db_osm -m 7 -u -n 10000 -i 1 $* > ../evaluation/uniform_dense_200M_llsm_level.txt


// 值得注意的一点是，重定向符号不会被clion识别，至少我还没找到方法。

// ycsb的情况
//./read_cold -f /mnt/db/dataset/amazon_reviews.txt -k 19 -v 64 -d /mnt/ssd/db_ar_random -m 7 -u -n 10000 --change_level_load $* --YCSB /home/zy/gitee_repos/db/ycsb/fast22-300M/workloada_op_key.txt --insert 33000000 -i 5 > ../evaluation/ar_llsm_workloada_op_key.txt


//-k 19 -v 64 -d ../ssd/ycsb-loada-10K-10K_random -m 7 -u -n 10000 --change_level_load -i 5 $* --YCSB ../db/ycsb/ycsb-e-10K-10K.txt > ../evaluation/ycsb_random_llsm_ycsb-e-10K-10K.txt
//-k 19 -v 64 -d ../ssd/ycsb-loada-10K-10K_random -m 7 -u -n 10000 --change_level_load -i 5 $* --YCSB ../db/ycsb/ycsb-a-10K-10K.txt > ../evaluation/ycsb_random_llsm_ycsb-a-10K-10K.txt

// 被kill的进程
//./read_cold -f ../db/sosd/books_200M.txt -k 16 -v 64 -d ../ssd/db_sosd_books_200M -m 7 -w > ../evaluation/sosd_books_200M_put.txt
// 违背kill的进程
//./read_cold -f ../db/sosd/books_200M_2M.txt -k 16 -v 64 -d ../ssd/db_sosd_books_200M_2M -m 7 -w > ../evaluation/sosd_books_200M_2M_put.txt
//./read_cold -f ../db/sosd/books_200M_2M.txt -k 16 -v 64 -d ../ssd/db_sosd_books_200M_2M -m 7 -u -n 10000 -i 5 --change_level_load $* > ../evaluation/sosd_books_200M_2M_llsm.txt
